package indi.kider.server.bill.service;

import indi.kider.common.constant.BillTypeCons;
import indi.kider.common.constant.SetLeaseCons;
import indi.kider.common.constant.StatusCons;
import indi.kider.common.model.bill.*;
import indi.kider.server.bill.mapper.BalanceTaskMapper;
import indi.kider.server.bill.model.BalanceCycle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pers.vankid.common.constant.DateTime;
import tk.mybatis.mapper.entity.Example;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;

/**
 * Created by shixj on 2018/5/4.
 * 任务插入状态基础状态（房租押金等）由外部业务逻辑去判断
 * 本服务仅提供基础的服务
 */
@Service
@Slf4j
public class BalanceTaskServiceImpl implements BalanceTaskService {

    public static final String INITAL_DATE = "1989-01-01";
    @Autowired
    private BalanceTaskMapper balanceTaskMapper;
    @Autowired
    private BillService billService;

    /**
     * 注册任务（指定归属，指定结算日期）
     *
     * @param taskRegist
     * @return
     */
    @Override
    @Transactional
    public void regist(TaskRegist taskRegist) throws Exception {
        log.info(taskRegist.toString());
        if(taskRegist.getWaterBlanceDate()==null)
            throw new Exception("水费结算周期不能为空！");
        if(taskRegist.getRentalBlanceCycle()==null)
            throw new Exception("租金结算周期不能为空！");
        registWaterTask(taskRegist);//水费
        registPowerTask(taskRegist);//电费
        registRentalTask(taskRegist);//租金
        registNetTask(taskRegist);//网费
        registEstateTask(taskRegist);//物业
        billService.balanceInitBill(taskRegist);//结算初始房租及押金
    }


    private void registWaterTask(TaskRegist taskRegist) {
        BalanceTask task = init(taskRegist.getRent().getId());
        task.setBalanceDate(taskRegist.getWaterBlanceDate());
        task.setBalanceCycle(SetLeaseCons.MONTH);
        task.setType(BillTypeCons.WATER);
        task.setPrice(taskRegist.getRent().getWaterCost());
        balanceTaskMapper.insert(task);
        task.setReading(taskRegist.getInitWaterReading());
        task.setReadingTime(new Date());
        saveInitTask(taskRegist, task);
    }

    private void registPowerTask(TaskRegist taskRegist) {
        BalanceTask task = init(taskRegist.getRent().getId());
        task.setBalanceDate(taskRegist.getPowerBlanceDate());
        task.setBalanceCycle(SetLeaseCons.MONTH);
        task.setType(BillTypeCons.POWER);
        task.setPrice(taskRegist.getRent().getPowerCost());
        balanceTaskMapper.insert(task);
        task.setReading(taskRegist.getInitPowerReading());
        task.setReadingTime(new Date());
        saveInitTask(taskRegist, task);
    }

    private void registRentalTask(TaskRegist taskRegist) {
        BalanceTask task = init(taskRegist.getRent().getId());
        task.setBalanceDate(taskRegist.getRentalBlanceDate());
        task.setBalanceCycle(taskRegist.getRentalBlanceCycle());
        task.setType(BillTypeCons.RENTAL);
        task.setPrice(taskRegist.getRent().getPriceSet());
        balanceTaskMapper.insert(task);
        saveInitTask(taskRegist, task);
    }

    private void registNetTask(TaskRegist taskRegist) {
        BalanceTask task = init(taskRegist.getRent().getId());
        task.setBalanceDate(taskRegist.getNetBlanceDate());
        task.setBalanceCycle(SetLeaseCons.MONTH);
        task.setType(BillTypeCons.NET);
        task.setPrice(taskRegist.getRent().getNetCost());
        balanceTaskMapper.insert(task);
        saveInitTask(taskRegist, task);
    }

    private void registEstateTask(TaskRegist taskRegist) {
        BalanceTask task = init(taskRegist.getRent().getId());
        task.setBalanceDate(taskRegist.getEstateBlanceDate());
        task.setBalanceCycle(SetLeaseCons.MONTH);
        task.setType(BillTypeCons.ESTATE);
        task.setPrice(taskRegist.getRent().getPropertyCosts());
        balanceTaskMapper.insert(task);
        saveInitTask(taskRegist, task);
    }

    private BalanceTask saveInitTask(TaskRegist taskRegist, BalanceTask task) {
        task.setId(null);
        task.setBalanceDate(DateTime.SHORT_DATE.format(taskRegist.getRent().getRentDate()));
        task.setStatus(StatusCons.HISTORY);
        balanceTaskMapper.insert(task);
        return task;
    }

    private BalanceTask init(Long rentId) {
        BalanceTask balanceTask = new BalanceTask();
        balanceTask.setId(null);
        balanceTask.setBillAttribution(rentId);
        balanceTask.setStatus(StatusCons.VALID);
        return balanceTask;
    }

    /**
     * 设置上月读数（或初始读数）
     *
     * @param balanceTask
     * @return
     */
    @Override
    @Transactional
    @Deprecated
    public BalanceTask setInitalReading(BalanceTask balanceTask) {
        balanceTask.setStatus(StatusCons.HISTORY);
        BalanceTask query = new BalanceTask();
        query.setBillAttribution(balanceTask.getBillAttribution());
        query.setBalanceDate(INITAL_DATE);
        BalanceTask task = queryOne(query);
        if (task == null)
            //初始读数
            balanceTaskMapper.insert(balanceTask);
        else {
            balanceTask.setId(task.getId());
            balanceTaskMapper.updateByPrimaryKeySelective(balanceTask);
            //已经有了的时候更新第一个任务
        }
        return balanceTask;
    }

    @Override
    @Transactional
    public BalanceTask update(BalanceTask balanceTask) {
        if (balanceTask.getId() == null || balanceTask.getId() == 0) {
            BalanceTask query = new BalanceTask();
            query.setBillAttribution(balanceTask.getBillAttribution());
            query.setStatus(StatusCons.VALID);
            BalanceTask task = balanceTaskMapper.selectOne(query);
            balanceTask.setId(task.getId());
        }
        balanceTaskMapper.updateByPrimaryKeySelective(balanceTask);
        return balanceTask;
    }

    @Override
    public BalanceTask queryOne(BalanceTask balanceTask) {
        return balanceTaskMapper.selectOne(balanceTask);
    }

    @Override
    public List<BalanceTask> query(BalanceTask balanceTask) {
        return balanceTaskMapper.select(balanceTask);
    }

    @Override
    public List<RentTask> getRentTask(RentTask rentTask) {
        return balanceTaskMapper.getRentTask(rentTask);
    }

    /**
     * 获取上一个任务
     *
     * @param balanceTask
     * @return
     */
    @Override
    public BalanceTask getPreTask(BalanceTask balanceTask) {
        BalanceTask query = new BalanceTask();
        query.setBillAttribution(balanceTask.getBillAttribution());
        query.setBalanceDate(getPreTaskDate(balanceTask));
        query.setType(balanceTask.getType());
        query.setStatus(StatusCons.HISTORY);
        BalanceTask pretask = queryOne(query);
        if (pretask == null)
            try {
                query.setBalanceDate(null);
                pretask = queryOne(query);
            } catch (Exception e) {
                pretask = null;
            }
        return pretask;
    }

    /**
     * 获取上次结算时间
     * @param balanceTask
     * @return
     */

    String getPreTaskDate(BalanceTask balanceTask){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate balanceDate = LocalDate.parse(balanceTask.getBalanceDate(), formatter);
        String preDate;
        if (balanceTask.getBalanceCycle().equals(SetLeaseCons.MONTH))
            preDate = balanceDate.minusMonths(1).format(formatter);
        else if (balanceTask.getBalanceCycle().equals(SetLeaseCons.QUARTER))
            preDate = balanceDate.minusMonths(3).format(formatter);
        else if (balanceTask.getBalanceCycle().equals(SetLeaseCons.HALF_YERA))
            preDate = balanceDate.minusMonths(6).format(formatter);
        else
            preDate = balanceDate.minusYears(1).format(formatter);
        return preDate;
    }

    public BalanceCycle getBalanceCycle(BalanceTask balanceTask) throws Exception {
        try {
            BalanceCycle balanceCycle = new BalanceCycle();
            Date balanceDate = DateTime.SHORT_DATE.parse(balanceTask.getBalanceDate());
            Date now = DateTime.SHORT_DATE.parse(DateTime.SHORT_DATE.format(new Date()));
            BalanceTask preTask = getPreTask(balanceTask);
            Date preBalanceDate = DateTime.SHORT_DATE.parse(preTask.getBalanceDate());
            if (now.getTime() > balanceDate.getTime()) {
                //补出账单
                balanceCycle.setActualBalanceStartTime(preBalanceDate);//实际结算开始时间为上期账单时间
                balanceCycle.setActualBalanceEndTime(balanceDate);//实际结算结束时间为当前未结时间
                balanceCycle.setExceptDays(
                        (balanceDate.getTime() - DateTime.SHORT_DATE.parse(getPreTaskDate(balanceTask)).getTime()) / (24 * 60 * 60 * 1000)
                );

            } else if (now.getTime() < balanceDate.getTime()) {
                //提前结算
                balanceCycle.setActualBalanceStartTime(preBalanceDate);//实际结算开始时间为上期账单时间
                balanceCycle.setActualBalanceEndTime(now);//实际结算结束时间为当前时间
                balanceCycle.setExceptDays(
                        (balanceDate.getTime() - preBalanceDate.getTime()) / (24 * 60 * 60 * 1000)
                );
            } else {
                //正常结算
                balanceCycle.setActualBalanceStartTime(preBalanceDate);
                balanceCycle.setActualBalanceEndTime(balanceDate);
                balanceCycle.setExceptDays(
                        (balanceCycle.getActualBalanceEndTime().getTime() - balanceCycle.getActualBalanceStartTime().getTime()) / (24 * 60 * 60 * 1000)
                );
            }
            balanceCycle.setActualDays(
                    (balanceCycle.getActualBalanceEndTime().getTime() - balanceCycle.getActualBalanceStartTime().getTime()) / (24 * 60 * 60 * 1000)
            );


            return balanceCycle;
        } catch (Exception e) {
            throw new Exception("结算任日期计算错误");
        }

    }

    /**
     * 生成并保存下一个任务
     *
     * @param balanceTask
     * @return
     */
    @Override
    @Transactional
    public BalanceTask saveSufTask(BalanceTask balanceTask) {
        BalanceTask sufTask = new BalanceTask();
        sufTask.setBillAttribution(balanceTask.getBillAttribution());//归属
        sufTask.setStatus(StatusCons.VALID);//有效
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate balanceDate = LocalDate.parse(balanceTask.getBalanceDate(), formatter);
        LocalDate newDate = balanceDate.plusMonths(1);
        if (balanceTask.getBalanceCycle().equals(SetLeaseCons.QUARTER))
            newDate = balanceDate.plusMonths(3);
        if (balanceTask.getBalanceCycle().equals(SetLeaseCons.HALF_YERA))
            newDate = balanceDate.plusMonths(6);
        else if (balanceTask.getBalanceCycle().equals(SetLeaseCons.YERA))
            newDate = balanceDate.plusYears(1);
        sufTask.setBalanceDate(newDate.format(formatter));//结算日期
        sufTask.setBalanceCycle(balanceTask.getBalanceCycle());
        sufTask.setPrice(balanceTask.getPrice());
        sufTask.setType(balanceTask.getType());
        balanceTaskMapper.insert(sufTask);
        return sufTask;
    }

    /**
     * 退租 结算产生账单
     *
     * @param billLeaseCancel
     */
    @Override
    public boolean leaseBalance(BillLeaseCancel billLeaseCancel) {
            billService.backDeposit(billLeaseCancel.getRentId());
            BalanceTask query = new BalanceTask();
            query.setStatus(StatusCons.VALID);
            query.setBillAttribution(billLeaseCancel.getRentId());
            List<BalanceTask> tasks = balanceTaskMapper.select(query);
            for (BalanceTask task : tasks) {
                    Integer type = task.getType();
                    if(!type.equals(BillTypeCons.RENTAL)){
                        //退租不出房租账单
                        BalanceTask taskPre = getPreTask(task);
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                        LocalDate balanceDatePre = LocalDate.parse(taskPre.getBalanceDate(), formatter);
                        LocalDate balanceDateNow = LocalDate.parse(task.getBalanceDate(), formatter);
                        Long daysAll = balanceDatePre.until(balanceDateNow, ChronoUnit.DAYS);
                        LocalDate now = LocalDate.now();
                        Long days = balanceDatePre.until(now, ChronoUnit.DAYS);
                        if (type == BillTypeCons.ESTATE || type == BillTypeCons.NET || type == BillTypeCons.RENTAL)
                            task.setPrice(taskPre.getPrice() * days / daysAll);
                        else if (type == BillTypeCons.WATER)
                            task.setReading(billLeaseCancel.getWaterReading());
                        else if (type == BillTypeCons.POWER)
                            task.setReading(billLeaseCancel.getPowerReading());
                        billService.balance(task, true);
                    }
            }
        return tasks.stream().anyMatch(task->task.getStatus().equals(StatusCons.ERROR));
    }

    /**
     * 注销任务
     *
     * @param rentId
     */
    @Override
    public void unregistTask(Long rentId) {
        BalanceTask task = new BalanceTask();
        task.setStatus(StatusCons.HISTORY);
        task.setBalanceDate(DateTime.SHORT_DATE.format(new Date()));
        Example example = new Example(BalanceTask.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("billAttribution",rentId).andEqualTo("status",StatusCons.VALID);
        balanceTaskMapper.updateByExampleSelective(task,example);
    }

    /**
     * 手动抄表处理异常任务
     *
     * @param task
     */
    @Override
    public Bill resovleErrorTask(BalanceTask task) throws Exception {
        if (task.getId() == null)
            throw new Exception("必须指定要处理的异常任务！");
        if (task.getType() == null)
            throw new Exception("必须指定要处理的异常任务类型！");
        BalanceTask balanceTask = balanceTaskMapper.selectByPrimaryKey(task.getId());
        balanceTask.setReading(task.getReading());
        balanceTask.setReadingTime(new Date());
        balanceTaskMapper.updateByPrimaryKeySelective(balanceTask);
        Bill bill = billService.balance(balanceTask, false);
        return bill;
    }
}
